//
/*******************************************************************************
        
        File name:     ChatContentView.swift
        Author:        danzel_duan
        Gitee.com:     https://gitee.com/danzel-duan/YBIMKit
        E-mail:        danzel_duan@163.com
        
        Description: Copyright © danzel_duan. All rights reserved.
        
        Created by danzel on 2018/12/31.
        
********************************************************************************/
       

import UIKit
import YBLibKit

public protocol ChatContentViewDataSource {
    func numberOfItems(in chatView: ChatContentView)
    func chatContentView(_ chatView: ChatContentView, itemAtIndexPath: IndexPath)
}

public protocol ChatContentViewDelegate: NSObjectProtocol {
    func refreshChatContentView(chatView: ChatContentView)
}

public class ChatContentView: UIView {

    public var chatViewLayout: ChatContentViewLayout
    public var chatContainerView: ChatContainerView
    
    public weak var messageDelegate: MessageDelegate?
    public weak var delegate: ChatContentViewDelegate?
    
    public var contentInset: UIEdgeInsets {
        set {
            chatContainerView.contentInset = newValue
        }
        get {
            return chatContainerView.contentInset
        }
    }
    
    internal var messageData: MessageData = MessageData.init([])
    
    private var chatContainerRegistedTypes: Set<String> = []
    fileprivate lazy var batchItems: Array<ChatContentViewUpdateItem> = []
    fileprivate lazy var batchRequiredCount: Int = 0
    
    init(frame: CGRect, layout: ChatContentViewLayout) {
        chatViewLayout = layout
        chatContainerView = ChatContainerView.init(frame: CGRect.zero, collectionViewLayout: layout)
        super.init(frame: frame)
        prepare()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
        print("ChatContentView=====================释放了")
    }
}

//MARK: 外界对消息进行增，删，更新，移动 插入
extension ChatContentView {
    /// 新增一条消息
    ///
    /// - Parameter newMessage: newMessage
    public func append(_ newMessage: Messageable) {
        insert(newMessage, at: messageData.count)
    }
    /// 新增一组消息
    ///
    /// - Parameter newMessages: newMessages
    public func append(contentsOf newMessages: Array<Messageable>) {
        insert(contentsOf: newMessages, at: messageData.count)
    }
    
    /// 删除一条消息
    ///
    /// - Parameter index: index
    public func remove(at index: Int) {
        batchBegin()
        batchItems.append(.remove(at: index))
        batchCommit()
    }
    
    /// 删除一组消息
    ///
    /// - Parameter indexs: indexs
    public func remove(contentOf indexs: Array<Int>) {
        batchBegin()
        batchItems.append(contentsOf: indexs.map({ .remove(at: $0)}))
        batchCommit()
    }
    
    /// 删除所有消息
    public func removeAll() {
        batchBegin()
        for index in 0..<messageData.count {
            batchItems.append(.remove(at: index))
        }
        batchCommit()
    }
    
    /// 更新一条消息
    ///
    /// - Parameters:
    ///   - newMessage: newMessage
    ///   - index: index
    public func update(_ newMessage: Messageable, at index: Int) {
        batchBegin()
        batchItems.append(.update(newMessage, at: index))
        batchCommit()
    }
    
    /// 将移到某个位置
    ///
    /// - Parameters:
    ///   - index1: index1
    ///   - index2: index2
    public func move(at index1: Int, to index2: Int) {
        batchBegin()
        batchItems.append(.move(at: index1, to: index2))
        batchCommit()
    }
    
    /// 插入一条消息在某个位置
    ///
    /// - Parameters:
    ///   - newMessage: newMessage
    ///   - index: index
    public func insert(_ newMessage: Messageable, at index: Int) {
        batchBegin()
        batchItems.append(.insert(newMessage, at: index))
        batchCommit()
    }
    
    /// 插入一组消息
    ///
    /// - Parameters:
    ///   - newMessages: newMessages
    ///   - index: index 
    public func insert(contentsOf newMessages: Array<Messageable>, at index: Int) {
        batchBegin()
        batchItems.append(contentsOf: newMessages.map({ .insert($0, at: index)}))
        batchCommit(true)
    }
}

//MARK: UICollectionView代理
extension ChatContentView: UICollectionViewDataSource, ChatContentViewDelegateLayout {
    public func collectionView(_ collectionView: UICollectionView, viewLayout collectionViewLayout: ChatContentViewLayout, itemAt indexPath: IndexPath) -> Messageable {
        return messageData[indexPath.item]
    }

    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return messageData.count
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let message = messageData[indexPath.item]
        let alignment = message.options.alignment
        let identifier = NSStringFromClass(type(of: message.content)) + ".\(alignment)"
        
        if !chatContainerRegistedTypes.contains(identifier) {
            chatContainerRegistedTypes.insert(identifier)
            chatContainerView.register(CustomMessageCell.self, forCellWithReuseIdentifier: identifier)
        }
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! CustomMessageCell
        return cell
    }
    
    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        guard let cell = cell as? CustomMessageCell else { return }
        cell.delegate = messageDelegate
        cell.updateValue()
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        guard let viewLayout = collectionViewLayout as? ChatContentViewLayout else {
            return .zero
        }
        guard let info = viewLayout.layoutAttributesInfoForItem(at: indexPath) else {
            return .zero
        }
        let size = info.layoutedForSize()
        return CGSize.init(width: frame.size.width, height: size.height)
    }
}

//MARK: 方法调用
extension ChatContentView {
    @objc
    private func onPullToRefresh() {
        delegate?.refreshChatContentView(chatView: self)
    }
    
    /// 停止刷新
    public func stopRefresh() {
        chatContainerView.yb_header?.endRefreshing()
    }
    
    /// 滑到最后一条消息
    public func scrollToLastMessage(animated: Bool = true) {
        let count = chatContainerView.numberOfItems(inSection: 0)
        if count > 0 {
            chatContainerView.scrollToItem(at: IndexPath.init(row: count - 1, section: 0), at: .bottom, animated: animated)
        }
    }
}

extension ChatContentView {
    private func batchBegin() {
        objc_sync_enter(batchItems)
        batchRequiredCount = max(batchRequiredCount + 1, 1)
        objc_sync_exit(batchItems)
    }
    
    private func batchCommit(_ isInsert: Bool = false) {
        objc_sync_enter(batchItems)
        batchRequiredCount = max(batchRequiredCount - 1, 0)
        guard batchRequiredCount == 0 else {
            objc_sync_exit(batchItems); return
        }
        
        let oldData = messageData
        let newData = MessageData.init()
        let updateItems = batchItems
        batchItems.removeAll()
        objc_sync_exit(batchItems)
        
        _ = chatContainerView.numberOfItems(inSection: 0)
        let update = ChatContentViewUpdate.init(newData: newData, oldData: oldData, updateItems: updateItems)
        messageData = newData
        chatContainerView.performBatchUpdates(with: update, isInsert, completion: nil)
    }
}

//设置UI
extension ChatContentView {
    internal func prepare() {
        let header = RefreshCommonHeader.headerRefreshing { [weak self] in  //先用这个方法解决循引用问题
            self?.onPullToRefresh()
        } as? RefreshCommonHeader
        header?.isRefreshingTitleHidden = true
        chatContainerView.yb_header = header
        
        chatContainerView.allowsSelection = false
        chatContainerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        chatContainerView.keyboardDismissMode = .onDrag
        chatContainerView.backgroundColor = UIColor.white
        chatContainerView.dataSource = self
        chatContainerView.delegate = self
        addSubview(chatContainerView)
        
        layout()
    }
    
    internal func layout() {
        addConstraint(layoutConstraintMake(chatContainerView, .left, .equal, self, .left))
        addConstraint(layoutConstraintMake(chatContainerView, .top, .equal, self, .top))
        addConstraint(layoutConstraintMake(chatContainerView, .right, .equal, self, .right))
        addConstraint(layoutConstraintMake(chatContainerView, .bottom, .equal, self, .bottom))
    }
}

extension ChatContentViewDelegate {
    public func refreshChatContentView(chatView: ChatContentView) {}
}
